package com.iwunu.auth.util;

import com.iwunu.auth.config.JwtConfig;
import com.iwunu.auth.listener.IJwtTokenCreateListener;
import com.iwunu.auth.listener.IJwtTokenParseListener;
import com.iwunu.auth.listener.IJwtTokenRefreshListener;
import com.iwunu.auth.vo.JwtRole;
import com.iwunu.auth.vo.JwtUserVo;
import io.jsonwebtoken.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * jwt 令牌工具类
 * @author vivi207
 *
 */
@Component
public class JwtTokenUtil {
	private static final Logger LOGGER = LoggerFactory.getLogger(JwtTokenUtil.class);
	
	@Autowired
	private JwtConfig jwtConfig;
	
	@Autowired(required=false)
	private IJwtTokenCreateListener jwtTokenCreateListener;
	
	@Autowired(required=false)
	private IJwtTokenParseListener jwtTokenParseListener;
	
	@Autowired(required=false)
	private IJwtTokenRefreshListener jwtTokenRefreshListener;
	
	/**
	 * 创建jwt令牌
	 * @param user
	 * @return
	 */
	public String create(JwtUserVo user) {
		return create(user, null);
	}
	
	/**
	 * 创建jwt令牌
	 * @param user
	 * @return
	 */
	public String create(JwtUserVo user, Map<String,Object> params) {
		LOGGER.debug("create jwt token by user:{}, params:{}, expiration():{}", user, params, jwtConfig.getExpiration());
		if(jwtTokenCreateListener!=null) {
			jwtTokenCreateListener.bofore(user);
		}
		Date expirationDate = new Date(System.currentTimeMillis() + jwtConfig.getExpiration());
		LOGGER.debug("jwt token expiration date:{}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(expirationDate));
		JwtBuilder jwtBuilder = Jwts.builder()
				.setSubject(user.getUsername())
				.setExpiration(expirationDate)
				.signWith(SignatureAlgorithm.HS512, jwtConfig.getSecretKey())
				.setIssuer(jwtConfig.getIssuer())
				.claim("id", user.getId())
				.claim("username", user.getUsername())
				.claim("name", user.getName())
				.claim("roles", JwtRoleJsonUtils.format(user.getAuthorities()));
		
		if(params!=null) {
			jwtBuilder.addClaims(params);
		}
		
		String token = jwtBuilder.compact();
		LOGGER.debug("create jwt token:{}", token);
		if(jwtTokenCreateListener!=null) {
			jwtTokenCreateListener.after(token, user);
		}
		return token;
	}
	
	/**
	 * 解析令牌
	 * @param token
	 * @param expirationExpand
	 * @return
	 */
	public Jws<Claims> parseJwt(String token, long expirationExpand) {
		LOGGER.debug("parse jwt token:{}, expirationExpand:{}", token, expirationExpand);
		if(jwtTokenParseListener!=null) {
			jwtTokenParseListener.bofore(token);
		}
		Jws<Claims> jws = Jwts
				.parser()
				.setSigningKey(jwtConfig.getSecretKey())
				.setAllowedClockSkewSeconds(expirationExpand)
				.parseClaimsJws(token);
		return jws;
	}
	
	/**
	 * 解析令牌用户信息
	 * @param jws
	 * @return
	 */
	public JwtUserVo parseJwt(Jws<Claims> jws) {
		Claims claims = jws.getBody();
		String username = claims.getSubject();
		Object id = claims.get("id");
		Object name = claims.get("name");

		List<JwtRole> roles = JwtRoleJsonUtils.parse((String)claims.get("roles"));
		
		JwtUserVo user = new JwtUserVo(username, roles);
		if(id!=null) {
			user.setId(Long.valueOf(id.toString()));
		}
		if(name!=null){
			user.setName(name.toString());
		}

		LOGGER.debug("parse jwt user:{}", user);
		return user;
	}
	
	/**
	 * 解析令牌用户信息
	 * @param token
	 * @return
	 */
	public JwtUserVo parse(String token) {
		return parse(token , jwtConfig.getExpirationExpand());
	}
	
	/**
	 * 解析令牌用户信息
	 * @param token
	 * @param expirationExpand
	 * @return
	 */
	public JwtUserVo parse(String token, long expirationExpand) {
		Jws<Claims> jws = parseJwt(token, expirationExpand);
		JwtUserVo user = parseJwt(jws);
		if(jwtTokenParseListener!=null) {
			jwtTokenParseListener.after(token , user);
		}
		return user;
	}
	
	/**
	 * 刷新令牌(超时时间)
	 * 令牌失效，返回旧令牌
	 * @return
	 */
	public String refresh(String token) {
		LOGGER.debug("refresh jwt token:{}", token);
		if(jwtTokenRefreshListener!=null) {
			jwtTokenRefreshListener.bofore(token);
		}
		Jws<Claims> jws = parseJwt(token, jwtConfig.getRefreshTime());
		Date expDate = jws.getBody().getExpiration();
		if(expDate.getTime() > System.currentTimeMillis()) {
			LOGGER.debug("令牌未超时,返回旧的令牌:{}", token);
			return token;
		}
		
		JwtUserVo user = parseJwt(jws);
		if(jwtTokenRefreshListener!=null) {
			jwtTokenRefreshListener.bofore(user);
		}
		
		String newToken = create(user);
		LOGGER.debug("create jwt new token:{}", newToken);
		if(jwtTokenRefreshListener!=null) {
			jwtTokenRefreshListener.after(newToken, token, user);
		}
		return newToken;
	}

	public JwtConfig getJwtConfig() {
		return jwtConfig;
	}

	public void setJwtConfig(JwtConfig jwtConfig) {
		this.jwtConfig = jwtConfig;
	}
}
